/*!	 canvasview.h
**	 Template Header
**
**	Copyright (c) 2002-2005 Robert B. Quattlebaum Jr., Adrian Bentley
**	Copyright (c) 2007, 2008 Chris Moore
**	Copyright (c) 2009 Carlos López
**
**	This package is free software; you can redistribute it and/or
**	modify it under the terms of the GNU General Public License as
**	published by the Free Software Foundation; either version 2 of
**	the License, or (at your option) any later version.
**
**	This package is distributed in the hope that it will be useful,
**	but WITHOUT ANY WARRANTY; without even the implied warranty of
**	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
**	General Public License for more details.
**
*/

#ifndef __SYNFIG_STUDIO_GTKMM_CANVASVIEW_H
#define __SYNFIG_STUDIO_GTKMM_CANVASVIEW_H

#include <glibmm/dispatcher.h>

#include <gtkmm/window.h>
#include <gtkmm/image.h>
#include <gtkmm/tooltip.h>
#include <gtkmm/table.h>
#include <gtkmm/statusbar.h>
#include <gtkmm/progressbar.h>
#include <gtkmm/button.h>
#include <gtkmm/menu.h>
#include <gtkmm/treeview.h>
#include <gtkmm/treestore.h>
#include <gtkmm/scrolledwindow.h>
#include <gtkmm/notebook.h>
#include <gdkmm/device.h>
#include <gtkmm/spinbutton.h>
#include <gtkmm/alignment.h>
#include <gtkmm/toolbar.h>
#include <gtkmm/toolitem.h>
#include <gtkmm/toolbutton.h>
#include <gtkmm/toggletoolbutton.h>
#include <gtkmm/separatortoolitem.h>

#include <synfigapp/canvasinterface.h>
#include <synfigapp/selectionmanager.h>

#include <ETL/clock>

#include <synfig/canvas.h>
#include <synfig/context.h>
#include <synfig/string.h>
#include <synfig/time.h>

#include "instance.h"
#include "dialogs/canvasproperties.h"
#include "dialogs/canvasoptions.h"
#include "render.h"
#include "cellrenderer/cellrenderer_timetrack.h"
#include "app.h"

#include "trees/layertreestore.h"
#include "trees/layertree.h"
#include "trees/childrentreestore.h"
#include "trees/childrentree.h"
#include "trees/keyframetreestore.h"
#include "trees/keyframetree.h"

#include "dialogs/dialog_waypoint.h"
#include "dialogs/dialog_keyframe.h"
#include "dialogs/dialog_preview.h"
#include "dialogs/dialog_soundselect.h"
#include "dials/framedial.h"
#include "dials/jackdial.h"
#include "dials/toggleducksdial.h"
#include "dials/resolutiondial.h"
#include "widgets/widget_keyframe_list.h"

#include "duckmatic.h"
#include <gtkmm/scale.h>

#include <gtkmm/uimanager.h>

#include "smach.h"

#include <set>
#include <map>
#include <gtkmm/toggleaction.h>
#include <gtkmm/radioaction.h>
#include <synfig/rect.h>

#include "adjust_window.h"

#include <synfig/transform.h>
#include <synfig/soundprocessor.h>

#include "docks/dockable.h"

#ifdef WITH_JACK
#include <jack/jack.h>
#include <jack/transport.h>
#endif

#ifndef DEBUGPOINT_CLASS
#if	_DEBUG
#define DEBUGPOINT_CLASS(x)		struct debugpointclass_ ## x { debugpointclass_ ## x () { DEBUGPOINT(); } ~debugpointclass_ ## x () { DEBUGPOINT(); } } badfthguae_ ## x ;
#else
#define DEBUGPOINT_CLASS(x)
#endif
#endif

namespace synfig
{
class TransformStack;
}

namespace studio
{

class CanvasViewUIInterface;
class CanvasViewSelectionManager;

class CellRenderer_TimeTrack;
class CellRenderer_ValueBase;
class WorkArea;

class Duckmatic;

class Widget_Enum;

class Preview;
struct PreviewInfo;
class AudioContainer;

class Widget_Sound;
class Widget_Timeslider;
class Widget_Time;

class Dock_Layers;
class Dock_Children;
class Dock_Keyframes;

/*!	\class studio::CanvasView
**	 \writeme
**
**	\writeme
*/
class CanvasView : public Dockable, public etl::shared_object
{
    friend class Dock_Layers;
    friend class Dock_Children;
    friend class Dock_Keyframes;

    friend class CanvasViewUIInterface;
    friend class CanvasViewSelectionManager;

    friend class Duckmatic;



public:

    typedef etl::handle<CanvasView> Handle;

    typedef etl::handle<const CanvasView> ConstHandle;

    typedef etl::loose_handle<CanvasView> LooseHandle;

    typedef LayerTreeStore::Model LayerTreeModel;

    typedef ChildrenTreeStore::Model ChildrenTreeModel;

    // Create an instance of this class whenever doing a longer task.
    /*! Make sure that you check the bool value of this class occasionally
    **	to make sure the action has not been canceled. */
    class IsWorking
    {
        CanvasView &canvas_view_;

    public:
        IsWorking(CanvasView &canvas_view_);
        ~IsWorking();
        operator bool()const;
    };
    friend class IsWorking;

    class ActivationIndex
    {
    private:
        static ActivationIndex last__;
    public:
        long long int activation_index;
        long long int creation_index;

        void create()
        {
            creation_index = ++last__.creation_index;
        }
        void activate()
        {
            activation_index = ++last__.activation_index;
        }

        explicit ActivationIndex(bool create = false): activation_index(0), creation_index(0)
        {
            if (create) {
                this->create();
            }
        }

        bool operator < (const ActivationIndex &other) const
        {
            if (activation_index < other.activation_index) {
                return true;
            }

            if (other.activation_index < activation_index) {
                return false;
            }

            return creation_index < other.creation_index;
        }
    };

    typedef synfigapp::CanvasInterface::Mode Mode;

    void set_grid_snap_toggle(bool flag)
    {
        grid_snap_toggle->set_active(flag);
    }
    void set_grid_show_toggle(bool flag)
    {
        grid_show_toggle->set_active(flag);
    }

public:
    WorkArea* get_work_area() const
    {
        return work_area;
    }

private:
    WorkArea *work_area;

    synfig::SoundProcessor soundProcessor;

    ActivationIndex activation_index_;

    synfig::Rect bbox;


    // State Machine
    Smach smach_;


    etl::loose_handle<Instance> instance_;
    etl::handle<synfigapp::CanvasInterface> canvas_interface_;
    synfig::ContextParams context_params_;


    // Sound and information to play it
    etl::handle<AudioContainer>		audio;
    studio::Widget_Sound			*disp_audio; // should this be put into thing too?

    sigc::connection				playcon;
    sigc::connection				stopcon;


    // TreeModel for the layers
    LayerTreeModel layer_tree_model;

    // TreeModel for the the children
    ChildrenTreeModel children_tree_model;


    std::map<synfig::String, Glib::RefPtr<Glib::ObjectBase> > ref_obj_book_;
    std::map<synfig::String, Gtk::Widget*> ext_widget_book_;

    // The time adjustment's scope is defined by the time_window adjustment
    Glib::RefPtr<Gtk::Adjustment> time_adjustment_;

    // The time_window adjustment governs the position of the time window on the whole time line
    Glib::RefPtr<studio::Adjust_Window> time_window_adjustment_;

    LayerTree *layer_tree;

    ChildrenTree *children_tree;

    KeyframeTree *keyframe_tree;

    Gtk::Widget *keyframe_tab_child;

    Gtk::ProgressBar *progressbar;
    Gtk::Statusbar *statusbar;

    Gtk::TreeRow children_canvas_row;
    Gtk::TreeRow children_valuenode_row;

    Gtk::Button *closebutton;
    Gtk::Button *stopbutton;
    Gtk::ToolButton *refreshbutton;
    Gtk::Button *treetogglebutton;  // not used
    Gtk::Notebook *notebook; // not used
    Gtk::Table *timebar;
    Gtk::Toolbar *displaybar;
    Widget_Enum *widget_interpolation;
    Gtk::ScrolledWindow *widget_interpolation_scroll;
    Gtk::ToggleButton *animatebutton;
    Gtk::ToggleButton *timetrackbutton;
    Gtk::VBox *timetrack;
    Gtk::Button *keyframebutton;
    Gtk::ToggleButton *pastkeyframebutton;
    Gtk::ToggleButton *futurekeyframebutton;
    bool toggling_animate_mode_;
    FrameDial *framedial;
    JackDial *jackdial;
    Gtk::ToggleButton *jackbutton;
    Widget_Time *offset_widget;
    ToggleDucksDial toggleducksdial;
    bool toggling_ducks_;
    ResolutionDial resolutiondial;
    bool changing_resolution_;
    Glib::RefPtr<Gtk::Adjustment> quality_adjustment_;
    Gtk::SpinButton *quality_spin;
    bool updating_quality_;
    Gtk::ToggleToolButton *show_grid;
    Gtk::ToggleToolButton *snap_grid;
    Gtk::ToolButton *render_options_button;
    Gtk::ToolButton *preview_options_button;
    bool toggling_show_grid;
    bool toggling_snap_grid;
    // Shows current time and allows edition
    Widget_Time *current_time_widget;
    void on_current_time_widget_changed();

    // Time slider class. Same than the Time track panel
    Widget_Timeslider *timeslider;

    // Keyframe list slider
    Widget_Keyframe_List *widget_kf_list;

    std::list<sigc::connection> duck_changed_connections;

    // Menu members
    Gtk::Menu parammenu;

    Gtk::UIManager::ui_merge_id merge_id_popup_;
    Gtk::UIManager::ui_merge_id merge_id_toolbar_;

    Glib::RefPtr<Gtk::ToggleAction> grid_snap_toggle;
    Glib::RefPtr<Gtk::ToggleAction> grid_show_toggle;

    Gtk::RadioButtonGroup quality_group;
    Gtk::RadioButtonGroup low_res_pixel_size_group;

    Glib::RefPtr<Gtk::ActionGroup> action_group;

    etl::handle<synfigapp::UIInterface> ui_interface_;
    etl::handle<synfigapp::SelectionManager> selection_manager_;

    bool is_playing_;
    sigc::connection playing_connection;
    etl::clock playing_timer;
    synfig::Time playing_time;

    sigc::signal<void> signal_deleted_;

    bool rebuild_ducks_queued;
    sigc::connection queue_rebuild_ducks_connection;

    bool jack_enabled;
    bool jack_actual_enabled;
    int jack_locks;
    bool jack_enabled_in_preview;
#ifdef WITH_JACK
    Glib::Dispatcher jack_dispatcher;
    jack_client_t *jack_client;
    bool jack_synchronizing;
    bool jack_is_playing;
    synfig::Time jack_time;
    bool toggling_jack;
#endif

    Glib::RefPtr<Gtk::ToggleAction> action_mask_bone_setup_ducks, action_mask_bone_recursive_ducks;



public:
    void queue_rebuild_ducks();
    sigc::signal<void>& signal_deleted()
    {
        return signal_deleted_;
    }

    Gtk::Menu mainmenu;

    bool duck_refresh_flag;
    bool duck_refresh_needed;

    // This is for the IsWorking class.
    int working_depth;

    bool cancel;



public:

    CanvasProperties canvas_properties;
    CanvasOptions canvas_options;
    RenderSettings render_settings;
    Dialog_Waypoint waypoint_dialog;
    Dialog_Keyframe keyframe_dialog;
    Dialog_Preview preview_dialog;
    Dialog_SoundSelect sound_dialog;



private:

    // Constructor is private to force the use of the "create()" constructor
    CanvasView(etl::loose_handle<Instance> instance, etl::handle<synfigapp::CanvasInterface> canvas_interface);

    // Constructor Helper

    // Constructor Helper

    // Constructor Helper

    // Constructor Helper - Initializes all of the menus
    void init_menus();

    bool duck_change_param(const synfig::Point &value, synfig::Layer::Handle layer, synfig::String param_name);

    void refresh_time_window();

    void time_was_changed();

    void refresh_rend_desc();

    void mask_bone_ducks();

    // Constructor Helper - Create the workarea and connect data and signal
    /*! \see popup_main_menu() */
    Gtk::Widget *create_work_area();

    Gtk::Widget *create_time_bar();

    Gtk::ToolButton* create_action_toolbutton(const Glib::RefPtr<Gtk::Action> &action);
    Gtk::SeparatorToolItem* create_tool_separator();
    Gtk::Widget* create_display_bar();

    // Pop up menu for the bezier (bline, draw) tool (?)
    void popup_param_menu_bezier(float location, synfigapp::ValueDesc value_desc)
    {
        popup_param_menu(value_desc, location, true);
    }

    // Pop up menu for the tools but not the bezier ones.
    void popup_param_menu(synfigapp::ValueDesc value_desc, float location = 0, bool bezier = false);

    void workarea_layer_selected(synfig::Layer::Handle layer);

    void selected_layer_color_set(synfig::Color color);

    void register_layer_type(synfig::Layer::Book::value_type &lyr, std::map<synfig::String, Gtk::Menu*>*);

    // Rebuilds the "new layer" menu
    void build_new_layer_menu(Gtk::Menu &menu);

    void decrease_low_res_pixel_size();
    void increase_low_res_pixel_size();
    void toggle_low_res_pixel_flag();
    void set_quality(int x);
    void toggle_show_grid();
    void toggle_snap_grid();

    void toggle_animatebutton();
    void toggle_timetrackbutton();

    void on_play_timeout();

    void interpolation_refresh();
    void on_interpolation_changed();
    void on_interpolation_event(GdkEvent *event);

    static void save_all();



public:
    ActivationIndex get_activation_index()
    {
        return activation_index_;
    }

    void activate();
    void deactivate();
    void present();

    bool jack_is_locked() const
    {
        return jack_locks > 0;
    }
    void jack_lock();
    void jack_unlock();
    bool get_jack_enabled_in_preview() const
    {
        return jack_enabled_in_preview;
    }
    void set_jack_enabled_in_preview(bool x)
    {
        jack_enabled_in_preview = x;
    }
#ifdef WITH_JACK
    bool get_jack_enabled() const
    {
        return jack_enabled;
    }
    bool get_jack_actual_enabled() const
    {
        return jack_actual_enabled;
    }
    void set_jack_enabled(bool value);
#endif

    synfig::Rect& get_bbox()
    {
        return bbox;
    }

    Glib::RefPtr<Glib::ObjectBase> get_ref_obj(const synfig::String& x);
    Glib::RefPtr<const Glib::ObjectBase> get_ref_obj(const synfig::String& x)const;
    void set_ref_obj(const synfig::String& x, Glib::RefPtr<Glib::ObjectBase> y);

    Glib::RefPtr<Gtk::TreeModel> get_tree_model(const synfig::String& x);
    Glib::RefPtr<const Gtk::TreeModel> get_tree_model(const synfig::String& x)const;
    void set_tree_model(const synfig::String& x, Glib::RefPtr<Gtk::TreeModel> y);

    Gtk::Widget* get_ext_widget(const synfig::String& x);
    void set_ext_widget(const synfig::String& x, Gtk::Widget* y);

    Gtk::UIManager::ui_merge_id get_popup_id();
    void set_popup_id(Gtk::UIManager::ui_merge_id popup_id);
    Gtk::UIManager::ui_merge_id get_toolbar_id();
    void set_toolbar_id(Gtk::UIManager::ui_merge_id toolbar_id);


    // Pop up menu for the main menu and the caret menu (not tools and not the bezier ones).
    /*! Signal handler for work_area->signal_popup_menu */
    /*! \see create_work_area(), popup_param_menu(), popup_param_menu_bezier() */
    void popup_main_menu();

    Smach& get_smach()
    {
        return smach_;
    }

    const Smach& get_smach()const
    {
        return smach_;
    }

    Smach::event_result process_event_key(EventKey x);

    void popup_layer_menu(synfig::Layer::Handle layer);

    virtual ~CanvasView();

    const synfig::ContextParams& get_context_params()const
    {
        return context_params_;
    }

    void set_context_params(const synfig::ContextParams &x)
    {
        context_params_ = x;
    }

    void set_mode(Mode x)
    {
        canvas_interface()->set_mode(x);
    }

    Mode get_mode()const
    {
        return canvas_interface()->get_mode();
    }

    Glib::RefPtr<Gtk::Adjustment> time_adjustment()
    {
        return time_adjustment_;
    }
    Glib::RefPtr<const Gtk::Adjustment> time_adjustment()const
    {
        return time_adjustment_;
    }

    Glib::RefPtr<studio::Adjust_Window> time_window_adjustment()
    {
        return time_window_adjustment_;
    }
    Glib::RefPtr<const studio::Adjust_Window> time_window_adjustment()const
    {
        return time_window_adjustment_;
    }

    etl::handle<synfigapp::UIInterface> get_ui_interface()
    {
        return ui_interface_;
    }

    etl::handle<synfigapp::SelectionManager> get_selection_manager()
    {
        return selection_manager_;
    }

    Glib::RefPtr<Gtk::TreeModel> layer_tree_store()
    {
        return get_tree_model("layers");
    }

    Glib::RefPtr<const Gtk::TreeModel> layer_tree_store()const
    {
        return get_tree_model("layers");
    }

    Glib::RefPtr<Gtk::TreeModel> children_tree_store()
    {
        return get_tree_model("children");
    }

    Glib::RefPtr<const Gtk::TreeModel> children_tree_store()const
    {
        return get_tree_model("children");
    }

    Glib::RefPtr<Gtk::TreeModel> keyframe_tree_store()
    {
        return get_tree_model("keyframes");
    }

    Glib::RefPtr<const Gtk::TreeModel> keyframe_tree_store()const
    {
        return get_tree_model("keyframes");
    }

    void set_time(synfig::Time t)
    {
        canvas_interface_->set_time(t);
    }

    synfig::Time get_time()
    {
        return canvas_interface_->get_time();
    }

    etl::handle<synfig::Canvas> get_canvas()const
    {
        return canvas_interface_->get_canvas();
    }

    etl::handle<Instance> get_instance()const
    {
        return instance_;
    }

    etl::handle<synfigapp::CanvasInterface> canvas_interface()
    {
        return canvas_interface_;
    }

    etl::handle<const synfigapp::CanvasInterface> canvas_interface()const
    {
        return canvas_interface_;
    }

    void add_actions_to_menu(Gtk::Menu *menu,   const synfigapp::Action::ParamList &param_list, synfigapp::Action::Category category = synfigapp::Action::CATEGORY_ALL)const;

    // Updates the title of the window
    void update_title();

    // Closes this document
    bool close_instance();

    // Closes this canvas view
    bool close_view();

    //	Stops the currently executing action
    /*! \see get_cancel_status(), reset_cancel_status(), IsWorking */
    void stop()
    {
        cancel = true;
    }

    // Returns the cancel status
    /*! \see stop(), reset_cancel_status(), IsWorking */
    bool get_cancel_status()const
    {
        return cancel;
    }

    // Resets the cancel status
    /*! \see stop(), get_cancel_status(), IsWorking */
    void reset_cancel_status()
    {
        cancel = false;
    }

    void new_child_canvas();

    // Rebuilds layer_tree_store_ from the Canvas. Maintains selected items.
    void rebuild_tables();

    // Builds layer_tree_store_ from the Canvas. Does not maintain selected items.
    void build_tables();

    // Refreshes the data for the tables
    void refresh_tables();



    // \writeme
    void rebuild_ducks();

    // bool add_to_ducks(synfigapp::ValueDesc value_desc, synfig::ParamDesc *param_desc=NULL);

    // Starts "playing" the animation in real-time
    void play();

    void play_async();
    void stop_async();

    // Shows the tables (Layer/Children)
    void show_tables();

    // Hides the tables (Layer/Children)
    void hide_tables();

    // Toggles the tables
    void toggle_tables();

    // Gets the table status
    bool tables_are_visible();

    // Shows the time bar
    void show_timebar();

    // Hides the time bar
    void hide_timebar();

    // Enables or disables interaction with the timebar
    void set_sensitive_timebar(bool sensitive);

    void time_zoom_in();
    void time_zoom_out();

    void add_layer(synfig::String x);

    void show_keyframe_dialog();
    void on_keyframe_toggle();
    void on_keyframe_description_set();

    void play_audio(float t);
    void stop_audio();

    void image_import();

    void on_waypoint_clicked_canvasview(synfigapp::ValueDesc, std::set<synfig::Waypoint, std::less<synfig::UniqueID> >, int button);

    void preview_option()
    {
        on_preview_option();
    }

    bool is_playing()
    {
        return is_playing_;
    }

    void update_quality();

    // Toggle given handle type
    // \Param[in]  type The Duckmatic::Type to toggle
    // \Sa             DuckMatic::set_type_mask(), DuckMatic::get_type_mask()
    void toggle_duck_mask(Duckmatic::Type type);
    // Toggle between none/last visible handles
    // \Sa             DuckMatic::set_type_mask_state(), DuckMatic::get_type_mask_state()
    void toggle_duck_mask_all();

    bool is_time_equal_to_current_frame(const synfig::Time &time, const synfig::Time &range = synfig::Time(0.0));



private:

    void on_select_layers();
    void on_unselect_layers();

    void on_input_device_changed(GdkDevice*);

    void on_hide();

    Gtk::Widget* create_tab_label();

    bool on_button_press_event(GdkEventButton *event);

    // bool on_children_tree_event(GdkEvent *event);

    bool on_keyframe_tree_event(GdkEvent *event);

    // void on_children_edited_value(const Glib::ustring&path_string,synfig::ValueBase value);

    void on_dirty_preview();

    bool on_children_user_click(int, Gtk::TreeRow, ChildrenTree::ColumnID);

    bool on_layer_user_click(int, Gtk::TreeRow, LayerTree::ColumnID);

    void on_mode_changed(synfigapp::CanvasInterface::Mode mode);

//	void on_layer_waypoint_clicked(const Glib::ustring &, synfig::ValueNode_Animated::WaypointList::iterator);

    // void on_children_waypoint_clicked(const Glib::ustring &, synfig::ValueNode_Animated::WaypointList::iterator);

    void on_waypoint_changed();

    void on_waypoint_delete();

    void on_refresh_pressed();

    void on_id_changed();

    void on_time_changed();



    void on_keyframe_add_pressed();

    void on_keyframe_duplicate_pressed();

    void on_keyframe_remove_pressed();

    void on_animate_button_pressed();

    void on_keyframe_button_pressed();
    void toggle_past_keyframe_button();
    void toggle_future_keyframe_button();

    void on_preview_option();
    void on_preview_create(const PreviewInfo &);

    void on_audio_option();
    void on_audio_file_change(const std::string &f);
    void on_audio_offset_change(const synfig::Time &t);

    void on_audio_file_notify();
    void on_audio_offset_notify();

    void on_layer_toggle(synfig::Layer::Handle);

    void on_edited_value(synfigapp::ValueDesc, synfig::ValueBase);

    void on_drop_drag_data_received(const Glib::RefPtr<Gdk::DragContext>& context, int x, int y, const Gtk::SelectionData& selection_data, guint info, guint time);

    bool on_audio_scrub();

    void on_play_pause_pressed();

#ifdef WITH_JACK
    void on_jack_offset_changed();
    void toggle_jack_button();

    synfig::Time get_jack_offset()const;
    void set_jack_offset(const synfig::Time &value);
#endif

    void on_meta_data_changed();

    // Keyboard event dispatcher following window priority
    bool on_key_press_event(GdkEventKey* event);
    bool focused_widget_has_priority(Gtk::Widget * focused);

protected:
    bool close_instance_when_safe();
    bool on_delete_event(GdkEventAny* event);



public:

    static etl::handle<studio::CanvasView> create(etl::loose_handle<Instance> instance, etl::handle<synfig::Canvas> canvas);
    static std::list<int>& get_pixel_sizes();

private:
#ifdef WITH_JACK
    void on_jack_sync();
    static int jack_sync_callback(jack_transport_state_t state, jack_position_t *pos, void *arg);
#endif

}; // END of class CanvasView

}; // END of namespace studio

#endif
